/*
 * Created: Author: Aléx Carvalho / Date: Feb 13, 2015
 * 
 * Changed: $Author: ac280593@gmail.com $ / $Date: 2015-06-05 00:21:41 +0000 (Fri, 05 Jun 2015) $
 */
package com.cpm.view.zk.tree;

import com.cpm.view.Element;
import com.cpm.wrapper.SolidObjects;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.UUID;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Tree;
import org.zkoss.zul.TreeModel;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.TreeitemRenderer;

public abstract class AbstractTree<T> implements Element<HtmlBasedComponent> {

    protected abstract void buildRoot(DefaultTreeNode<T> root);

    protected abstract void render(Treeitem item, DefaultTreeNode<T> node, int i);

    private final Tree tree = new Tree();

    private TreeRender treeRender;

    protected String arrowId = UUID.randomUUID().toString();

    public AbstractTree() {

        Clients.evalJavaScript("$('body').append(\"<i id='" + arrowId + "'></i>\")");

        tree.setVflex(true);
        tree.setStyle("border: none;");
        defaultRender();
    }

    public void addSelectionListener(EventListener listener) {
        tree.addEventListener(Events.ON_SELECT, e -> listener.onEvent(e));
    }

    private void defaultRender() {

        tree.setItemRenderer(new DefaultTreeItemRenderer());

        treeRender = (Treeitem item, DefaultTreeNode node, int i) -> {
            render(item, node, i);
        };
    }

    public void buildTree() {
        DefaultTreeNode root = new DefaultTreeNode("ROOT");

        DefaultTreeModel model = new DefaultTreeModel(root);

        tree.setModel(model);

        buildRoot(root);
    }

    public DefaultTreeNode<T> getSelectedNode() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            if (!model.getSelection().isEmpty()) {
                return (DefaultTreeNode) model.getSelection().iterator().next();
            }
        }

        return null;
    }

    public void setSelectedNode(DefaultTreeNode<T> value) {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            model.addToSelection(value);
        }
    }

    public T getSelectedValue() {
        DefaultTreeNode<T> node = getSelectedNode();

        return node != null ? node.getData() : null;
    }

    public void setSelectedValue(T value) {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            if (!model.getSelection().isEmpty()) {
                for (Iterator iterator = model.getSelection().iterator(); iterator.hasNext();) {
                    Object next = iterator.next();
                    if (next instanceof DefaultTreeNode) {
                        DefaultTreeNode node = (DefaultTreeNode) next;
                        if (value.equals(node.getData())) {
                            setSelectedNode(node);
                            return;
                        }
                    }

                }
            }
        }
    }

    public void expand() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            expandNode((DefaultTreeNode<T>) tree.getModel().getRoot());
        }
    }

    public void expandAll() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            DefaultTreeNode root = (DefaultTreeNode) tree.getModel().getRoot();

            for (Enumeration<DefaultTreeNode> e = root.breadthFirstEnumeration(); e.hasMoreElements();) {
                DefaultTreeNode node = e.nextElement();

                if (!node.isLeaf()) {
                    model.addOpenObject(node);
                }
            }
        }
    }

    public void expandNode(DefaultTreeNode<T> value) {
        DefaultTreeNode node = (DefaultTreeNode) value;

        if (node != null && tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            if (node.getParent() == null || model.isObjectOpened(node)) {
                for (Enumeration<DefaultTreeNode> e = node.children(); e.hasMoreElements();) {
                    expandNode(e.nextElement());
                }
            }

            else if (!node.isLeaf()) {
                model.addOpenObject(node);
            }
        }
    }

    public void collapse() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            collapseNode((DefaultTreeNode<T>) tree.getModel().getRoot());
        }
    }

    public void collapseAll() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeNode root = (DefaultTreeNode) tree.getModel().getRoot();

            for (Enumeration<DefaultTreeNode> e = root.breadthFirstEnumeration(); e.hasMoreElements();) {
                DefaultTreeNode node = e.nextElement();

                if (!node.isLeaf()) {
                    collapseNode(node);
                }
            }
        }
    }

    public void collapseNode(DefaultTreeNode<T> value) {
        DefaultTreeNode node = (DefaultTreeNode) value;
        if (node != null && tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            if (node.getParent() == null || model.isObjectOpened(node)) {
                boolean expanded = false;

                for (Enumeration<DefaultTreeNode> e = node.children(); e.hasMoreElements();) {
                    DefaultTreeNode child = e.nextElement();

                    if (model.isObjectOpened(child)) {
                        collapseNode(child);

                        expanded = true;
                    }
                }

                if (!expanded) {
                    model.removeOpenObject(node);
                }
            }
        }
    }

    public boolean isMultipleSelection() {
        return tree.isMultiple();
    }

    public void enableMultipleSelection() {
        tree.setMultiple(true);
    }

    public void disableMultipleSelection() {
        tree.setMultiple(false);
    }

    public TreeModel getModel() {
        return tree.getModel();
    }

    public void renderItems() {
        tree.onInitRender();
    }

    @Override
    public HtmlBasedComponent getElement() {
        return tree;
    }

    private class DefaultTreeItemRenderer implements TreeitemRenderer<DefaultTreeNode> {

        @Override
        public void render(Treeitem item, DefaultTreeNode node, int i) throws Exception {
            SolidObjects.notNull(treeRender);
            treeRender.render(item, node, i);
        }
    }
}
